iT邦幫忙

2025 iThome 鐵人賽

DAY 24
0
Software Development

Go Clean Architecture API 開發全攻略系列 第 24

[Day 24] 從 MVC 到六角形架構:一個後端工程師的思考轉變

  • 分享至 

  • xImage
  •  

對於絕大多數後端工程師而言,MVC(Model-View-Controller)是我們學習 Web 開發時接觸的第一個,也是最經典的架構模式。無論是 Ruby on Rails, Django, Laravel 還是 Spring MVC,都深深地烙印著 MVC 的思想。

MVC 是一個偉大且成功的模式,但它主要誕生於一個以「伺服器端渲染頁面」為主的時代。當我們的應用程式演變為提供純粹 API 的服務、微服務時,我們需要反思:MVC 還是最佳選擇嗎?隨著需求的多元化與系統規模的擴大,傳統 MVC 架構在維護性、可測試性與彈性上逐漸暴露出限制。

回顧經典 MVC

在經典的 MVC 模式中,三個元件的職責是:

  • Model (模型):代表資料和業務邏輯。負責與資料庫互動,並提供資料給 View。
  • View (視圖):使用者介面。在傳統 Web 應用中,它負責將 Model 的資料渲染成 HTML。
  • Controller (控制器):接收使用者的 HTTP 請求,呼叫 Model 來更新狀態,並選擇一個 View 來產生回應。

在我們的 API 服務中,「View」的角色被簡化為 JSON 回應。這種簡化雖然讓開發流程更直接,但也讓 MVC 的分層意義變得模糊,特別是在缺乏複雜 UI 的情境下。

MVC 在現代後端開發中的常見困境

隨著業務變複雜,許多遵循 MVC 模式的專案會不自覺地陷入以下困境:

  1. 臃腫的控制器 (Fat Controllers):大量的業務邏輯,因為無處安放,最終都被塞進了 Controller 的方法中。Controller 不僅要處理 HTTP 請求和回應,還要負責資料驗證、流程編排、呼叫多個 Model。這使得 Controller 變得極其臃腫,難以測試和維護。當 Controller 變得過於龐大時,任何小的需求變動都可能牽一髮動全身,導致維護成本大幅提升。

  2. 貧血的模型 (Anemic Models):與此同時,Model 逐漸退化成一個只有 Getters/Setters 的「資料袋」,所有的業務邏輯都被抽離到 Controller 或某些 Service 層中,完全違背了物件導向中「資料與操作相結合」的初衷。這種設計讓 Model 失去了主動性,導致業務規則分散在各處,降低了程式碼的可讀性與一致性。

  3. 以資料庫為中心:在許多 MVC 框架中(特別是 ActiveRecord 風格的),Model 與資料庫的表結構是緊緊綁定的。這導致整個應用的設計都圍繞著資料庫 schema 展開,業務邏輯隱性地依賴於資料庫的實作細節。這使得「脫離資料庫來測試業務邏輯」變得異常困難。當我們想要替換資料庫或進行單元測試時,往往會發現業務邏輯與資料存取層糾纏不清。

六角形架構如何解決這些問題

我們的專案所採用的六角形架構,正是為了解決上述問題而設計的。

  • 針對臃腫的控制器:我們的 Controller 極其輕薄。它的唯一職責是「翻譯」HTTP 請求,然後委派給 Usecase。所有的業務流程都在 Usecase 中,職責清晰。這樣的設計讓 Controller 易於維護與測試,並且能專注於協調請求與回應。

  • 針對貧血的模型:我們的 Domain 層是「充血」的。User 物件不僅有資料,更有保護其內部一致性的驗證邏輯和業務方法。它是應用程式中真正「聰明」的部分。這種設計讓業務規則集中於 Domain 物件,提升了程式碼的可讀性、可維護性與一致性。

  • 針對以資料庫為中心:這是兩者最本質的區別。在我們的架構中,業務領域(Domain)才是宇宙的中心,而不是資料庫。資料庫只是一個可以被隨時替換的「基礎設施」,是一個被動實現了 Repository 介面的「Adapter」。依賴倒轉原則確保了我們的核心業務邏輯對 GORM、對 MySQL 一無所知。這讓我們能夠輕鬆地替換資料存取技術,或在測試時使用記憶體資料庫模擬,極大提升了系統的彈性與可測試性。

思考的轉變:從「分層」到「內外之分」

從 MVC 到六角形架構,最核心的是思維模式的轉變:

  • MVC 的心智模型是「水平分層」:UI 層 -> 業務層 -> 資料層。資料流從上到下,像一個三層蛋糕。這種分層方式雖然直觀,但容易讓業務邏輯與技術細節混雜,導致層與層之間的界線模糊。
  • 六角形架構的心智模型是「內外之分」:核心是「內部」(Domain, Usecase),其他一切(HTTP, Database, Redis, ...)都是「外部」。依賴的箭頭永遠從「外部」指向「內部」。這種設計強調「業務邏輯」與「技術細節」的徹底分離,讓核心邏輯不受外部變動影響。

我們不再問「這個邏輯該放在 MVC 的哪一層?」,而是問「這個邏輯是核心業務,還是與外部技術互動的細節?」。如果是前者,它就屬於「內部」;如果是後者,它就是一個需要被隔離在外的「Adapter」。這種思維方式讓我們在設計系統時,能夠更聚焦於業務本身,並且更容易因應技術棧的變動。

總結

MVC 是一個優秀的模式,對於快速開發簡單的、以 UI 為中心的應用程式,它依然非常高效。然而,當我們需要建置一個複雜的、需要長期維護的、且可能需要適應不同技術(例如,未來可能需要支援 gRPC 或命令列觸發)的後端服務時,六角形架構所提供的高可測試性、高靈活性和邊界清晰性,展現出了無與倫比的優勢。

我們專案至今所享受到的所有好處——輕鬆的單元測試、清晰的模組劃分——都源於我們在專案之初,就選擇了這條「以業務為核心,與技術細節解耦」的道路。六角形架構不僅提升了開發效率,更讓我們能夠從容應對未來的技術挑戰與業務變革,真正實現了「穩定核心、彈性外圍」的理想架構。


上一篇
[Day 23] 為何 `internal` 目錄對你的 Go 專案至關重要
系列文
Go Clean Architecture API 開發全攻略24
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言